﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Linq.Expressions;

namespace XEnamel.Python
{
	public class ScopeManager : IPythonScopeManager
	{
		#region Constants

		private const string SYMBOL_FORMAT = @"[A-Za-z_][A-Za-z_0-9\.]*";

		#endregion

		#region Variables

		private Stack<Dictionary<string, ParameterExpression>> _context;
		private Stack<Dictionary<ParameterExpression, Expression>> _translation;
		private Stack<Dictionary<string, LabelTarget>> _labels;

		#endregion

		#region Constructors

		public ScopeManager()
		{
			_context = new Stack<Dictionary<string, ParameterExpression>>();
			_translation = new Stack<Dictionary<ParameterExpression, Expression>>();
			_labels = new Stack<Dictionary<string, LabelTarget>>();
		}

		#endregion

		#region Methods

		public void EnterScope()
		{
			_context.Push(new Dictionary<string, ParameterExpression>());
			_translation.Push(new Dictionary<ParameterExpression, Expression>());
			_labels.Push(new Dictionary<string, LabelTarget>());
		}

		public void LeaveScope()
		{
			_context.Pop();
			_translation.Pop();
			_labels.Pop();
		}

		public bool IsSymbolNameValid(string name)
		{
			return Regex.Match(name, SYMBOL_FORMAT).Success;
		}

		public Expression FindSymbol(string name)
		{
			foreach (var map in _context)
			{
				if (map.ContainsKey(name))
				{
					return map[name];
				}
			}
			return null;
		}

		public Expression CreateSymbol(string name, Type type = null)
		{
			var expr = Expression.Parameter(type ?? typeof(object), name);
			_context.Peek().Add(name, expr);
			return expr;
		}

		public LabelTarget FindLabel(string name)
		{
			foreach (var map in _labels)
			{
				if (map.ContainsKey(name))
				{
					return map[name];
				}
			}
			return null;
		}

		public LabelTarget CreateLabel(string name, Type type = null)
		{
			var expr = Expression.Label(type ?? typeof(object), name);
			_labels.Peek().Add(name, expr);
			return expr;
		}

		public LabelTarget VisitLabelTarget(string name, Type type = null)
		{
			return FindLabel(name) ?? CreateLabel(name, type);
		}

		#endregion
	}
}